package com.hzgj.bcl.soa.rpc;


import com.hzgj.bcl.concurrent.FutureListener;
import com.hzgj.bcl.soa.ChainStack;
import com.hzgj.bcl.soa.config.MethodDef;
import com.hzgj.bcl.soa.rpc.client.RequestOptions;
import com.hzgj.bcl.soa.rpc.message.Message;
import com.hzgj.bcl.soa.rpc.retry.RetryPolicy;
import com.hzgj.bcl.soa.spi.MessageHandleFactory;
import com.hzgj.bcl.soa.util.Constants;
import com.hzgj.bcl.soa.util.NotifyingFuture;
import com.hzgj.bcl.soa.util.ServiceLoaderUtil;

import java.lang.reflect.Method;
import java.util.Objects;

import static com.hzgj.bcl.soa.util.Constants.DEFAULT_LOADBALANCE;
import static com.hzgj.bcl.soa.util.Constants.DEFAULT_REQUESTTIMEOUT;
import static com.hzgj.bcl.soa.util.Constants.MESSAGE_HEADER_BEGIN;

public class RpcDispatcher extends MessageDispatcher {

    public RpcDispatcher(ChainStack stack) {
        super(stack);
    }

    public <T> T callServiceMethod(Method method, Object[] args, RequestOptions options) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("method=" + method + ", args=" + args + ",options=" + options);
        }
        T retval = super.sendMessage(getMessage(method, args, options), options);
        if (logger.isTraceEnabled()) {
            logger.trace("retval: " + retval);
        }
        return retval;
    }

    public <T> NotifyingFuture<T> callServiceMethodWithFuture(Method method, Object[] args, RequestOptions
            options, FutureListener<T> listener) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("method=" + method + ", args=" + args + ",options=" + options);
        }
        return super.sendMessageWithFuture(getMessage(method, args, options), options, listener);
    }

    private Message getMessage(Method method, Object[] args, RequestOptions options) {
        MessageHandleFactory factory = ServiceLoaderUtil.getMessageBuildFactory(options.getReferenceDef().getProtocol());
        Message msg = factory.buildRequestMessage(method, args, options);
        int readTimeout = options.getReferenceDef().getRequestTimeoutInMills();
        MethodDef methodDef = options.getMethodDef();
        if (methodDef != null && methodDef.getRequestTimeoutInMills() != DEFAULT_REQUESTTIMEOUT) {
            readTimeout = methodDef.getRequestTimeoutInMills();
        }
        RetryPolicy retryPolicy = options.getReferenceDef().getRetryPolicy();
        if (methodDef != null && methodDef.getRetryPolicy() != null) {
            retryPolicy = methodDef.getRetryPolicy();
        }

        String lb = options.getReferenceDef().getLoadbalance();
        if (methodDef != null && !Objects.equals(methodDef.getLoadbalance(), DEFAULT_LOADBALANCE)) {
            lb = methodDef.getLoadbalance();
        }
        return msg.putHeader(Constants.MESSAGE_HEADER_ASYN, options.getReferenceDef().isAsync())
                .putHeader(Constants.MESSAGE_TYPE, Message.REQ)
                .putHeader(Constants.MESSAGE_HEADER_METHOD, method.getName())
                .putHeader(Constants.MESSAGE_HEADER_RETRYPOLICY, retryPolicy)
                .putHeader(Constants.MESSAGE_HEADER_REQUESTTIMEOUTINMILLS, readTimeout)
                .putHeader(Constants.MESSAGE_HEADER_LBRULE, lb)
                .putHeader(Constants.MESSAGE_HEADER_SERVICE_VERSION, options.getReferenceDef().getVersion())
                .putHeader(Constants.MESSAGE_HEADER_SERVICE_GROUP, options.getReferenceDef().getGroup())
                .putHeader(MESSAGE_HEADER_BEGIN, options.getBegin())
                .putHeader(Constants.MESSAGE_HEADER_PROTOCOL, options.getReferenceDef().getProtocol())
                .putHeader(Constants.MESSAGE_HEADER_CLUSTER, options.getReferenceDef().getCluster())
                .putHeader(Constants.MESSAGE_HEADER_TARGET_SERVICE, options.getReferenceDef().getService());
    }
}
